﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices.WindowsRuntime;
using StyleMVVM.Data;
using StyleMVVM.Data.Activation;
using StyleMVVM.Data;

#if !DOT_NET
using Windows.Foundation.Metadata;
#endif

namespace StyleMVVM.DependencyInjection
{
	/// <summary>
	/// This is the base IOC Container for StyleMVVM.
	/// All methods are thread safe unless marked.
	/// It does not however handle calling IDisposable.Dispose()
	/// that onus falls on the consumer
	/// </summary>
	public interface IDependencyInjectionContainer
	{
		/// <summary>
		/// If a concrete type is requested and it is not registered an export strategy will be created.
		/// </summary>
		bool AutoRegisterUnknown { get; set; }

		/// <summary>
		/// Defines what environment the IDependencyInjectionContainer is operating in
		/// </summary>
		ExportEnvironment Environment { get; set; }

		/// <summary>
		///  The instance of the activation service the Container is using.
		/// </summary>
		IActivationService ActivationService { get; }

		/// <summary>
		/// Starts the DependencyInjectionContainer (nothing is offically exported till this time)
		/// </summary>
		void Start();

		/// <summary>
		/// Starts the DependencyInjectionContainer (nothing is offically exported till this time)
		/// </summary>
		void Start(bool launchUI);

		/// <summary>
		/// Called Once the UI has launched, core exports marked as UI will be created
		/// </summary>
		void Launch();

		/// <summary>
		/// Shutsdown the DependencyInjectionContainer and all shared obects 
		/// </summary>
		void Shutdown();

		/// <summary>
		/// Creates a new IInjectionContext that contains all the current global conditions.
		/// </summary>
		/// <returns></returns>
		IInjectionContext CreateInjectionContext();

		/// <summary>
		/// Creates a new IInjectionContext that contains all the current global conditions.
		/// </summary>
		/// <returns></returns>
		IInjectionContext CreateInjectionContext(IDisposalScope disposalScope);

		/// <summary>
		/// Creates a new disposable injection context
		/// </summary>
		/// <returns></returns>
		IDisposableInjectionContext CreateDisposableInjectionContext();

		/// <summary>
		/// Creates a new disposable injection context
		/// </summary>
		/// <param name="disposalScope"></param>
		/// <returns></returns>
		IDisposableInjectionContext CreateDisposableInjectionContext(IDisposalScope disposalScope);
		/// <summary>
		/// Allows you to add a global condition to all injection contexts.
		/// Note: this Essentially is a short cut wrapper around AddContionValue where the value is true
		/// </summary>
		/// <param name="conditionName"></param>
		/// <param name="exportCondition"></param>
		void AddCondition(ExportConditionProviderDelegate exportCondition);

		/// <summary>
		/// Allows you to add a global condition value to all injection contexts
		/// </summary>
		/// <param name="exportConditionValue"></param>
		void AddConditionValue(ExportConditionValueProviderDelegate exportConditionValue);

		/// <summary>
		/// Removes a condition value from the container
		/// </summary>
		/// <param name="exportConditionValue"></param>
		void RemoveConditionValue(ExportConditionValueProviderDelegate exportConditionValue);

		/// <summary>
		/// Processes an object for any Imports and statisfies them.
		/// </summary>
		/// <param name="injectionObject"></param>
		void Inject(object injectionObject);

		/// <summary>
		/// Locates an object with a particular exportName.  
		/// </summary>
		/// <param name="exportName">exportName to look for</param>
		/// <param name="constructorParams">parameters to the constructor if there are any</param>
		/// <returns>returns export or null</returns>
		object Locate(string exportName, params object[] constructorParams);

		/// <summary>
		/// Locate an object and specify the injection context, allowing you to have finer control over the injection process
		/// </summary>
		/// <param name="exportName"></param>
		/// <param name="injectionContext"></param>
		/// <param name="constructorParams"></param>
		/// <returns></returns>
		object LocateWithContext(IInjectionContext injectionContext, string exportName, params object[] constructorParams);

		/// <summary>
		/// Locates an Export of a particular Type
		/// </summary>
		/// <param name="exportType">Export Type</param>
		/// <param name="constructorParams">parameters to the constructor if there are any</param>
		/// <returns>returns export or null</returns>
		object LocateByType(Type exportType, params object[] constructorParams);

		/// <summary>
		/// Lcoate an object by type while specifying the injection context
		/// </summary>
		/// <param name="injectionContext"></param>
		/// <param name="exportType"></param>
		/// <param name="constructorParams"></param>
		/// <returns></returns>
		object LocateByTypeWithContext(IInjectionContext injectionContext, Type exportType, params object[] constructorParams);

		/// <summary>
		/// Locates an Export of a particular Type
		/// </summary>
		/// <param name="exportType">Export Type</param>
		/// <param name="constructorParams">parameters to the constructor if there are any</param>
		/// <returns>returns export or null</returns>
		[DefaultOverload]
		object LocateByType(TypeWrapper exportType, params object[] constructorParams);

		/// <summary>
		/// Locates all exports of Type T
		/// </summary>
		/// <returns>a collection of T (empty collection if none)</returns>
		IEnumerable<object> LocateAllByType(Type exportType);

		/// <summary>
		/// Located all exports of Type T and specify the injection context
		/// </summary>
		/// <param name="injectionContext"></param>
		/// <param name="exportType"></param>
		/// <returns></returns>
		IEnumerable<object> LocateAllByTypeWithContext(IInjectionContext injectionContext, Type exportType);

		/// <summary>
		/// Locate all exports of type exportType into the out list
		/// </summary>
		/// <param name="injectionContext"></param>
		/// <param name="exportType"></param>
		/// <param name="outList"></param>
		IEnumerable LocateAllByTypeWithContext(IInjectionContext injectionContext, Type exportType, IList outList);

			/// <summary>
		/// Locates all exports of Type T
		/// </summary>
		/// <returns>a collection of T (empty collection if none)</returns>
		[DefaultOverload]
		IEnumerable<object> LocateAllByType(TypeWrapper exportType);

		/// <summary>
		/// Lcoate all exports of name ExportName
		/// </summary>
		/// <param name="exportName">name to locate</param>
		/// <returns>a collection of T (empty collection if none)</returns>
		IEnumerable<object> LocateAll(string exportName);

		/// <summary>
		/// Locate all export using the name specified and the injection context provided.
		/// </summary>
		/// <param name="injectionContext"></param>
		/// <param name="exportName"></param>
		/// <returns></returns>
		IEnumerable<object> LocateAllWithContext(IInjectionContext injectionContext, string exportName);

		/// <summary>
		/// Locates all exports of Type T
		/// </summary>
		/// <returns>a collection of T (empty collection if none)</returns>
		IEnumerable<object> LocateAllByType(Type exportType, FilterExport filterMethod);

		/// <summary>
		/// Locates all exports of Type T
		/// </summary>
		/// <returns>a collection of T (empty collection if none)</returns>
		[DefaultOverload]
		IEnumerable<object> LocateAllByType(TypeWrapper exportType, FilterExport filterMethod);

		/// <summary>
		/// Lcoate all exports of name ExportName
		/// </summary>
		/// <param name="exportName">name to locate</param>
		/// <param name="filterMethod">method that can filter the returns based on IExportStrategy</param>
		/// <returns>a collection of T (empty collection if none)</returns>
		IEnumerable<object> LocateAll(string exportName, FilterExport filterMethod);

		/// <summary>
		/// Locate the type that is exporting this name.
		/// </summary>
		/// <param name="exportName">the name to locate</param>
		/// <returns>export Type or null</returns>
		Type LocateExportType(string exportName);

		/// <summary>
		/// Locate the type that is exporting this name.
		/// </summary>
		/// <param name="exportType">the name to locate</param>
		/// <returns>export Type or null</returns>
		Type LocateExportTypeByType(Type exportType);

		/// <summary>
		/// Locate the type that is exporting this name.
		/// </summary>
		/// <param name="exportType">the name to locate</param>
		/// <returns>export Type or null</returns>
		[DefaultOverload]
		Type LocateExportTypeByType(TypeWrapper exportType);

		/// <summary>
		/// Locate the type that is exporting this name.
		/// </summary>
		/// <param name="exportName">the name to locate</param>
		/// <returns>export Type or null</returns>
		IEnumerable<Type> LocateAllExportTypes(string exportName);

		/// <summary>
		/// Locate the type that is exporting this name.
		/// </summary>
		/// <returns>export Type or null</returns>
		IEnumerable<Type> LocateAllExportTypesByType(Type exportType);

		/// <summary>
		/// Locate the type that is exporting this name.
		/// </summary>
		/// <param name="exportName">the name to locate</param>
		/// <returns>export Type or null</returns>
		[DefaultOverload]
		IEnumerable<Type> LocateAllExportTypesByType(TypeWrapper exportName);

		/// <summary>
		/// Register all Exported types in an assembly
		/// </summary>
		/// <param name="assembly">assembly to register</param>
		/// <returns></returns>
		IExportAssemblyConfiguration RegisterAssembly(object assembly);

		/// <summary>
		/// Register a type for export
		/// </summary>
		/// <param name="exportType"></param>
		/// <returns></returns>
		IExportConfiguration Register(Type exportType);

		/// <summary>
		/// Register a type for export
		/// </summary>
		/// <param name="exportType"></param>
		/// <returns></returns>
		[DefaultOverload]
		IExportConfiguration Register(TypeWrapper exportType);

#if DOT_NET

	/// <summary>
	/// Scans a directory for assemblies to register
	/// </summary>
	/// <param name="directory"></param>
		void ScanDirectoryForExports(string directory);
#endif

		/// <summary>
		/// Register an export and the export function will be called at locate time
		/// </summary>
		/// <param name="exportType">Type to export, this is used as the type for import, initialize and Activate method</param>
		/// <param name="activatedType"></param>
		/// <param name="exportFunction">method to construct the export</param>
		/// <returns></returns>
		IExportConfiguration RegisterExportFunction(Type activatedType, ExportFunction exportFunction);

		/// <summary>
		/// Register an export and the export function will be called at locate time
		/// </summary>
		/// <param name="exportFunction">method to construct the export</param>
		/// <returns></returns>
		IExportConfiguration RegisterExportJS(ExportFunction exportFunction);

		/// <summary>
		/// Register an activatoin factory to be called when scanning assemblies.
		/// </summary>
		/// <param name="newFactory"></param>
		void RegisterFactory(IExportStrategyFactory newFactory);

		/// <summary>
		/// Unregister an activatoin factory.
		/// </summary>
		/// <param name="oldFactory"></param>
		void UnregisterFactory(IExportStrategyFactory oldFactory);

		/// <summary>
		/// Black lists a particular export (Fullname)
		/// </summary>
		/// <param name="exportType"></param>
		void BlackListExport(string exportType);

		/// <summary>
		/// Black list a particular export by Type
		/// </summary>
		/// <param name="exportType"></param>
		void BlackListExportType(Type exportType);

		/// <summary>
		/// Black list a particular export by Type
		/// </summary>
		/// <param name="exportType"></param>
		[DefaultOverload]
		void BlackListExportType(TypeWrapper exportType);

		/// <summary>
		/// This method tells the container to process all new registraion since last the last time it was called.
		/// </summary>
		void ProcessRegistration();

		/// <summary>
		/// Returns a list of all known strategies.
		/// </summary>
		/// <returns>returns all known strategies</returns>
		IEnumerable<IExportStrategy> GetAllStrategies();

		/// <summary>
		/// Finds the best matching strategy exported by the name provided
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		[DefaultOverload]
		IExportStrategy GetStrategy(string name);

		/// <summary>
		/// Finds the best matching strategy exported by the name provided
		/// </summary>
		/// <param name="exportType"></param>
		/// <returns></returns>
		IExportStrategy GetStrategy(Type exportType);

		/// <summary>
		/// Get the list of exported strategies sorted by best option.
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		[DefaultOverload]
		IEnumerable<IExportStrategy> GetStrategies(string name);

		/// <summary>
		/// Get the list of exported strategies sorted by best option.
		/// </summary>
		/// <param name="exportType"></param>
		/// <returns></returns>
		IEnumerable<IExportStrategy> GetStrategies(Type exportType);

		/// <summary>
		/// Adds a new strategy to the container
		/// </summary>
		/// <param name="addStrategy"></param>
		void AddStrategy(IExportStrategy strategy);

		/// <summary>
		/// Allows the caller to remove a strategy from the container
		/// </summary>
		/// <param name="knownStrategy">strategy to remove</param>
		void RemoveStrategy(IExportStrategy knownStrategy);

		/// <summary>
		/// This property allows you to create your own comparison method.
		/// </summary>
		ExportComparison Comparision { get; set; }

		/// <summary>
		/// You can handle this event to provide an export strategy for any unknown export
		/// </summary>
		event EventHandler<UnknownExportArgs> ResolveUnknownExport;
	}
}